#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>

#include "Task.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"
#include "Tread.hpp"

class ThreadDate
{
public:
    ThreadDate(const std::string name)
        : _name(name)
    {
    }

    ~ThreadDate()
    {
    }

public:
    std::string _name;
};
const int defaultnum = 5;
template <class T>
class ThreadPool
{

private:
    ThreadPool(int thread_num = defaultnum)
    :_thread_num(thread_num)
    {
        pthread_cond_init(&p_cond,nullptr);
        pthread_mutex_init(&_mutex,nullptr);
        for(int i = 0;i < thread_num;i++)
        {
            std::string thename = "thread -";
            thename += std::to_string(i + 1);
            ThreadDate td(thename);

            _threads.emplace_back(thename,std::bind(&ThreadPool<T>::ThreadRun  ,this,std::placeholders::_1),td);
            lg.LogMessage(Info, "%s is created...\n", thename.c_str());
        }
    }
    ThreadPool(const ThreadPool<T>&tp) = delete;
    const ThreadPool<T>& operator=(const ThreadPool<T>) = delete;
public:
    static ThreadPool<T> * Getinstance()
    {
        if(instance == nullptr)
        {
            LockGuard lock(&sig_lock);
            if(instance == nullptr)
            {
                lg.LogMessage(Info, "创建单例成功...\n");
                instance = new ThreadPool<T>();
            }
        }
        return instance;
    }
    bool Start()
    {
        for(auto &ts:_threads)
        {
            ts.Start();
            lg.LogMessage(Info, "%s is running ...\n", ts.Threadname().c_str());
        }
        return true;
    }

    void ThreadWait(const ThreadDate& td)
    {
        lg.LogMessage(Debug, "no task, %s is sleeping...\n", td._name.c_str());
        pthread_cond_wait(&p_cond,&_mutex);
    }

    void ThreadWaitup()
    {
        pthread_cond_signal(&p_cond);
    }

    void ThreadRun(ThreadDate& td)
    {
        while(true)
        {
            T t;
            {
                LockGuard lock(&_mutex);
                while(_q.empty())
                {
                    ThreadWait(td);
                    lg.LogMessage(Debug, "thread %s is wakeup\n", td._name.c_str());
                }
                t = _q.front();
                _q.pop();
            }
            t();
            lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
                          td._name.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());
        }
    }

    void push(T& in)
    {
        {
            lg.LogMessage(Debug, "other thread push a task, task is : %s\n", in.PrintTask().c_str());
            LockGuard lock(&_mutex);
            _q.push(in);
            ThreadWaitup();

        }
    }
    void Wait()
    {
        for(auto &thread:_threads)
        {
            thread.Join();
        }
    }
    ~ThreadPool()
    {
        pthread_cond_destroy(&p_cond);
        pthread_mutex_destroy(&_mutex);
    }
private:
    std::queue<T> _q;
    std::vector<Thread<ThreadDate>> _threads;
    pthread_mutex_t _mutex;
    int _thread_num;
    pthread_cond_t p_cond;

    static ThreadPool<T> *instance;
    static pthread_mutex_t sig_lock;
};

template<class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template<class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;